/*
 ------------------------------------------------------------------------------
 Copyright (C) 2006-2007 Team Blur.

 This file is part of the Quantum Engine source code.

 The Quantum Engine source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Quantum Engine source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Quantum Engine source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


// sv_game.cpp -- interface to game library


#include "sv_local.h"


static void *		sg_libHandle;
static sgImport_t	sg_framework;

sgExport_t *		sg = NULL;


/*
 ==================
 SG_AddReliableCommand
 ==================
*/
static void SG_AddReliableCommand (int clientNum, const char *fmt, ...){

	char	command[MAX_STRING_LENGTH];
	va_list	argPtr;
	int		len;
 
	if (clientNum < 0 || clientNum >= sv_maxClients->integerValue)
		Com_Error(false, "SG_AddReliableCommand: bad clientNum (%i)", clientNum);

	va_start(argPtr, fmt);
	len = Str_VSPrintf(command, sizeof(command), fmt, argPtr);
	va_end(argPtr);

	if (len == -1)
		Com_Error(false, "SG_AddReliableCommand: command string overflow");

	SV_SendReliableCommand(&svs.clients[clientNum], "%s", command);
}

/*
 ==================
 SG_BroadcastReliableCommand
 ==================
*/
static void SG_BroadcastReliableCommand (const char *fmt, ...){

	char	command[MAX_STRING_LENGTH];
	va_list	argPtr;
	int		len;

	va_start(argPtr, fmt);
	len = Str_VSPrintf(command, sizeof(command), fmt, argPtr);
	va_end(argPtr);

	if (len == -1)
		Com_Error(false, "SG_BroadcastReliableCommand: command string overflow");

	SV_BroadcastReliableCommand("%s", command);
}

/*
 ==================
 SG_DropClient
 ==================
*/
static void SG_DropClient (int clientNum, const char *reason){

	if (clientNum < 0 || clientNum >= sv_maxClients->integerValue)
		Com_Error(false, "SG_DropClient: bad clientNum (%i)", clientNum);

	SV_DropClient(&svs.clients[clientNum], reason);
}


// ============================================================================


/*
 ==================
 SG_SetupFramework
 ==================
*/
#undef CopyFile
#undef LoadLibrary
static void SG_SetupFramework (void){

	// Common functions
	sg_framework.odSystem.Printf = Com_Printf;
	sg_framework.odSystem.DPrintf = Com_DPrintf;
	sg_framework.odSystem.Error = Com_Error;

	// Memory functions
	sg_framework.odMemory.Alloc = Mem_Alloc;
	sg_framework.odMemory.ClearedAlloc = Mem_ClearedAlloc;
	sg_framework.odMemory.Free = Mem_Free;
	sg_framework.odMemory.TagFree = Mem_TagFree;
	sg_framework.odMemory.Size = Mem_Size;
	sg_framework.odMemory.TagSize = Mem_TagSize;
	sg_framework.odMemory.CopyString = Mem_CopyString;

	// File system functions
	sg_framework.odFileSystem.Read = FS_Read;
	sg_framework.odFileSystem.Write = FS_Write;
	sg_framework.odFileSystem.Printf = FS_Printf;
	sg_framework.odFileSystem.Seek = FS_Seek;
	sg_framework.odFileSystem.Tell = FS_Tell;
	sg_framework.odFileSystem.Flush = FS_Flush;
	sg_framework.odFileSystem.ForceFlush = FS_ForceFlush;
	sg_framework.odFileSystem.OpenFile = FS_OpenFile;
	sg_framework.odFileSystem.CloseFile = FS_CloseFile;
	sg_framework.odFileSystem.ReadFile = FS_ReadFile;
	sg_framework.odFileSystem.FreeFile = FS_FreeFile;
	sg_framework.odFileSystem.WriteFile = FS_WriteFile;
	sg_framework.odFileSystem.CopyFile = FS_CopyFile;
	sg_framework.odFileSystem.RenameFile = FS_RenameFile;
	sg_framework.odFileSystem.RemoveFile = FS_RemoveFile;
	sg_framework.odFileSystem.FileExists = FS_FileExists;
	sg_framework.odFileSystem.ListFiles = FS_ListFiles;
	sg_framework.odFileSystem.ListFilteredFiles = FS_ListFilteredFiles;
	sg_framework.odFileSystem.FreeFileList = FS_FreeFileList;

	// Command system functions
	sg_framework.odCmdSystem.AddCommand = Cmd_AddCommand;
	sg_framework.odCmdSystem.RemoveCommand = Cmd_RemoveCommand;
	sg_framework.odCmdSystem.Argc = Cmd_Argc;
	sg_framework.odCmdSystem.Argv = Cmd_Argv;
	sg_framework.odCmdSystem.Args = Cmd_Args;
	sg_framework.odCmdSystem.TokenizeString = Cmd_TokenizeString;
	sg_framework.odCmdSystem.ExecuteText = Cmd_ExecuteText;

	// CVar system functions
	sg_framework.odCVarSystem.Get = CVar_Get;
	sg_framework.odCVarSystem.SetString = CVar_SetString;
	sg_framework.odCVarSystem.SetFloat = CVar_SetFloat;
	sg_framework.odCVarSystem.SetInteger = CVar_SetInteger;
	sg_framework.odCVarSystem.FindVariable = CVar_FindVariable;
	sg_framework.odCVarSystem.GetVariableString = CVar_GetVariableString;
	sg_framework.odCVarSystem.GetVariableFloat = CVar_GetVariableFloat;
	sg_framework.odCVarSystem.GetVariableInteger = CVar_GetVariableInteger;
	sg_framework.odCVarSystem.SetVariableString = CVar_SetVariableString;
	sg_framework.odCVarSystem.SetVariableFloat = CVar_SetVariableFloat;
	sg_framework.odCVarSystem.SetVariableInteger = CVar_SetVariableInteger;
	sg_framework.odCVarSystem.AllowCheats = CVar_AllowCheats;
	sg_framework.odCVarSystem.GetModifiedFlags = CVar_GetModifiedFlags;
	sg_framework.odCVarSystem.SetModifiedFlags = CVar_SetModifiedFlags;
	sg_framework.odCVarSystem.ClearModifiedFlags = CVar_ClearModifiedFlags;

	// Parser functions
	sg_framework.odParser.ReadToken = PS_ReadToken;
	sg_framework.odParser.UnreadToken = PS_UnreadToken;
	sg_framework.odParser.ReadDouble = PS_ReadDouble;
	sg_framework.odParser.ReadFloat = PS_ReadFloat;
	sg_framework.odParser.ReadUnsignedInt = PS_ReadUnsignedInt;
	sg_framework.odParser.ReadSignedInt = PS_ReadSignedInt;
	sg_framework.odParser.ExpectTokenString = PS_ExpectTokenString;
	sg_framework.odParser.ExpectTokenType = PS_ExpectTokenType;
	sg_framework.odParser.CheckTokenString = PS_CheckTokenString;
	sg_framework.odParser.CheckTokenType = PS_CheckTokenType;
	sg_framework.odParser.PeekTokenString = PS_PeekTokenString;
	sg_framework.odParser.PeekTokenType = PS_PeekTokenType;
	sg_framework.odParser.SkipUntilString = PS_SkipUntilString;
	sg_framework.odParser.SkipRestOfLine = PS_SkipRestOfLine;
	sg_framework.odParser.SkipBracedSection = PS_SkipBracedSection;
	sg_framework.odParser.ScriptWarning = PS_ScriptWarning;
	sg_framework.odParser.ScriptError = PS_ScriptError;
	sg_framework.odParser.ResetScript = PS_ResetScript;
	sg_framework.odParser.EndOfScript = PS_EndOfScript;
	sg_framework.odParser.SetScriptFlags = PS_SetScriptFlags;
	sg_framework.odParser.SetPunctuationsTable = PS_SetPunctuationsTable;
	sg_framework.odParser.LoadScriptFile = PS_LoadScriptFile;
	sg_framework.odParser.LoadScriptMemory = PS_LoadScriptMemory;
	sg_framework.odParser.FreeScript = PS_FreeScript;

	// System functions
	sg_framework.odSystem.LoadLibrary = Sys_LoadLibrary;
	sg_framework.odSystem.FreeLibrary = Sys_FreeLibrary;
	sg_framework.odSystem.GetProcAddress = Sys_GetProcAddress;
	sg_framework.odSystem.Milliseconds = Sys_Milliseconds;
	sg_framework.odSystem.GetClockTicks = Sys_GetClockTicks;
	sg_framework.odSystem.ClockTicksPerSecond = Sys_ClockTicksPerSecond;
	sg_framework.odSystem.GetProcessorId = Sys_GetProcessorId;
	sg_framework.odSystem.GetProcessorString = Sys_GetProcessorString;

	// Collision functions
	sg_framework.odCollision.LoadMap = CM_LoadMap;
	sg_framework.odCollision.RegisterModel = CM_RegisterModel;
	sg_framework.odCollision.SetupBoxModel = CM_SetupBoxModel;
	//sg_framework.odCollision.InstantiateDynamicModel = CM_InstantiateDynamicModel;
	sg_framework.odCollision.GetModelContents = CM_GetModelContents;
	sg_framework.odCollision.GetModelBounds = CM_GetModelBounds;
	sg_framework.odCollision.Contents = CM_Contents;
	sg_framework.odCollision.Trace = CM_Trace;
	sg_framework.odCollision.PointInLeaf = CM_PointInLeaf;
	sg_framework.odCollision.LeafCluster = CM_LeafCluster;
	sg_framework.odCollision.LeafArea = CM_LeafArea;
	sg_framework.odCollision.LeafCanSeePortalSky = CM_LeafCanSeePortalSky;
	sg_framework.odCollision.ClusterPVS = CM_ClusterPVS;
	sg_framework.odCollision.PointInPVS = CM_PointInPVS;
	sg_framework.odCollision.BoundsInPVS = CM_BoundsInPVS;
	sg_framework.odCollision.NumPortals = CM_NumPortals;
	sg_framework.odCollision.FindPortal = CM_FindPortal;
	sg_framework.odCollision.SetPortalState = CM_SetPortalState;
	sg_framework.odCollision.GetPortalState = CM_GetPortalState;
	sg_framework.odCollision.WritePortalBits = CM_WritePortalBits;
	sg_framework.odCollision.AreasAreConnected = CM_AreasAreConnected;
	sg_framework.odCollision.NumAreas = CM_NumAreas;
	sg_framework.odCollision.PointInArea = CM_PointInArea;
	sg_framework.odCollision.BoundsInAreas = CM_BoundsInAreas;
	sg_framework.odCollision.NumPortalsInArea = CM_NumPortalsInArea;
	sg_framework.odCollision.GetPortalFromArea = CM_GetPortalFromArea;
	sg_framework.odCollision.WriteAreaBits = CM_WriteAreaBits;

	// Editor functions
	sg_framework.odEditor.LaunchEditor = ED_LaunchEditor;
	sg_framework.odEditor.CloseEditor = ED_CloseEditor;
	sg_framework.odEditor.DrawEditBox = ED_DrawEditBox;
	sg_framework.odEditor.SetLightCallbacks = ED_SetLightCallbacks;
	sg_framework.odEditor.EditLightProperties = ED_EditLightProperties;
	sg_framework.odEditor.SetReverbCallbacks = ED_SetReverbCallbacks;
	sg_framework.odEditor.EditReverbProperties = ED_EditReverbProperties;

	// Server functions
	sg_framework.AddReliableCommand = SG_AddReliableCommand;
	sg_framework.BroadcastReliableCommand = SG_BroadcastReliableCommand;
	sg_framework.DropClient = SG_DropClient;
}

/*
 ==================
 SG_Init
 ==================
*/
void SG_Init (void){

	GETSGAMEAPI	GetSGameAPI;

	// Free anything we have
	if (sg_libHandle)
		SG_Shutdown();

	// Set up the framework for the library
	SG_SetupFramework();

	// Load the game library
	Com_Printf("Loading server game library...\n");

	sg_libHandle = Sys_LoadLibrary("sgame");
	if (!sg_libHandle)
		Com_Error(true, "Could not load server game library");

	// Get the game API
	GetSGameAPI = (GETSGAMEAPI)Sys_GetProcAddress(sg_libHandle, "GetSGameAPI");
	if (!GetSGameAPI){
		Sys_FreeLibrary(sg_libHandle);
		sg_libHandle = NULL;

		Com_Error(true, "Could not get server game API");
	}

	sg = GetSGameAPI(&sg_framework);

	if (sg->apiVersion != SGAME_API_VERSION)
		Com_Error(true, "Server game interface version is %i, expected version %i", sg->apiVersion, SGAME_API_VERSION);

	// Initialize the game
	sg->Init();
}

/*
 ==================
 SG_Shutdown
 ==================
*/
void SG_Shutdown (void){

	if (!sg_libHandle)
		return;

	// Shutdown the game
	sg->Shutdown();

	// Free the game library
	Com_Printf("Unloading server game library...\n");

	Sys_FreeLibrary(sg_libHandle);
	sg_libHandle = NULL;

	sg = NULL;
}
